feature: add AccountServiceTest unit tests with Mockito#245
feature: add AccountServiceTest unit tests with Mockito#245devin-ai-integration[bot] wants to merge 1 commit into
Conversation
Co-Authored-By: Matthew Guerra <matthew.guerra@cognition.ai>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
| void loadUserByUsername_shouldThrowWhenUsernameNotFound() { | ||
| when(accountRepository.findByUsername("ghost")).thenReturn(Optional.empty()); | ||
|
|
||
| assertThrows(RuntimeException.class, | ||
| () -> accountService.loadUserByUsername("ghost")); | ||
| } |
There was a problem hiding this comment.
🚩 loadUserByUsername test checks RuntimeException but method contract declares UsernameNotFoundException
The loadUserByUsername_shouldThrowWhenUsernameNotFound test asserts RuntimeException.class, which is technically correct for what the implementation does — findAccountByUsername at AccountService.java:87 calls orElseThrow(() -> new RuntimeException("Account not found")) at AccountService.java:35, throwing before the UsernameNotFoundException check on line 88-89 (which is dead code). However, the UserDetailsService contract specifies that UsernameNotFoundException should be thrown. This means the test is encoding a pre-existing bug in the service rather than testing the expected Spring Security contract. If the service is later fixed to throw UsernameNotFoundException, this test will break.
Was this helpful? React with 👍 or 👎 to provide feedback.
Debug
| void loadUserByUsername_shouldThrowWhenUsernameNotFound() { | ||
| when(accountRepository.findByUsername("ghost")).thenReturn(Optional.empty()); | ||
|
|
||
| assertThrows(RuntimeException.class, | ||
| () -> accountService.loadUserByUsername("ghost")); | ||
| } |
There was a problem hiding this comment.
🚩 Test asserts RuntimeException instead of UsernameNotFoundException for loadUserByUsername
The loadUserByUsername_shouldThrowWhenUsernameNotFound test asserts RuntimeException.class is thrown. The Spring Security UserDetailsService contract expects a UsernameNotFoundException to be thrown when a user is not found. However, the actual service code at AccountService.java:87 delegates to findAccountByUsername() which throws a plain RuntimeException("Account not found") — so the test is accurately reflecting the current (arguably incorrect) service behavior. Since UsernameNotFoundException extends AuthenticationException extends RuntimeException, fixing the service to throw UsernameNotFoundException would not break this test either, making the test weaker than it could be. This is a pre-existing service issue, not a test bug.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Agreed — this is intentional. The test asserts RuntimeException because that's what the current service throws (findAccountByUsername -> RuntimeException("Account not found")), and the task spec for this test (#15) explicitly asks to "assert RuntimeException is thrown." Since UsernameNotFoundException extends RuntimeException, this assertion remains valid if the service is later hardened to throw the more specific Spring Security exception. Leaving the service behavior unchanged as it's out of scope for this test-only PR.
Summary
Adds
src/test/java/com/example/bankapp/service/AccountServiceTest.java— 16 pure unit tests covering every method inAccountService, using@ExtendWith(MockitoExtension.class)with@MockAccountRepository/TransactionRepository/PasswordEncoderand@InjectMocks AccountService.These are mock-only tests (no Spring context / no MySQL), so they run independently of the DB-backed
BankappApplicationTests.contextLoads.Coverage:
deposit: balance increase + repo saves;ArgumentCaptorasserts Transaction type"Deposit"and amount.withdraw: balance decrease;RuntimeException("Insufficient funds")on overdraw (asserts no saves); Transaction type"Withdrawal".transferAmount: debit sender / credit recipient (mocksfindByUsername(toUsername)); throws on insufficient funds; throws"Recipient account not found"on empty Optional; exactly twotransactionRepository.save()calls.registerAccount: creates account withBigDecimal.ZERObalance and encoded password (captor); throws"Username already exists"when present.findAccountByUsername: returns account / throws"Account not found".loadUserByUsername: returnsUserDetailswith correct username/password / throws when not found.getTransactionHistory: returns transactions fromfindByAccountId.Run locally with
./mvnw -Dtest=AccountServiceTest test→Tests run: 16, Failures: 0, Errors: 0.Link to Devin session: https://app.devin.ai/sessions/c0c80e1f3cb9488a9bd6694801249426
Requested by: @matthewguerra-cog
Devin Review
aa5bcb6